home *** CD-ROM | disk | FTP | other *** search
/ Developer Helper 1: Phil & Dave's Excellent CD / Excellent CD HFS.raw / Moof / Goodies / DTS Goodies / Hier⁄Popup Menu Demos / HierMenus.p < prev    next >
Text File  |  1989-04-13  |  11KB  |  307 lines

  1. {*************************************************************************
  2.  
  3. Hierarchical Menu Example
  4.  
  5. This program is a simple example of how to add hierarchical menus to your
  6. application. A few tips:
  7.  
  8. The toughest part is defining the menu item that "owns" the submenu. The
  9. item must be defined with a command-key of $1B, and a "mark" character
  10. whose value is the ID of the submenu. The submenu is defined just like
  11. a normal menu, but is inserted in the Menu Manager's private "hierarchical"
  12. menu list by specifying -1 as the second parameter to InsertMenu. 
  13.  
  14. It's easiest to declare all your menus (and submenus) as resources (as
  15. opposed to creating them at runtime with NewMenu), even if you're creating
  16. a submenu that will initially contain no items (like the Font submenu in 
  17. this example). This simplifies your program's handling of submenus (note
  18. that in this example, since the submenus' IDs follow the regular menu's
  19. ID, the only special handling of submenus is the test (in SetUpMenus) to
  20. decide whether to use 0 or -1 as the second parameter to InsertMenu!).
  21.  
  22. Submenus owned by applications' menus' IDs should always be in the range
  23. 1 through 235 (inclusive); desk accessories should use IDs 236 through
  24. 255, and should leave their menus and submenus in the menu bar only when
  25. they're active (that is, call InsertMenu when handling an activate event,
  26. and DeleteMenu when handling a deactivate event). This really applies to 
  27. all menus; it's most important for pop-up and submenus, however. All this
  28. to prevent conflicts (it may be helpful to note that in searching through 
  29. the menu list, the hierarchical list [containing all submenus & pop-up
  30. menus] is searched first).
  31.  
  32. This particular example is meant to show the mechanics of creating and
  33. handling events from submenus; in the interest of simplicity, certain nice
  34. features (like checkmarking the current font and styles, setting the 
  35. style of "real" font sizes in the Font submenu to "outline", etc) have been
  36. omitted.
  37.  
  38. Have fun.
  39.  
  40. Note: the current version does not actually call SysEnvirons to
  41. determine whether System 4.1 is running. This will be fixed, but
  42. in the meantime, run this with System 
  43. *************************************************************************}
  44.  
  45. PROGRAM HierMenus;
  46. {*
  47.  * Hierarchical Menu Example
  48.  * Bryan Stearns 05May87 
  49.  *
  50.  * (C) 1987 Apple Computer, Inc.
  51.  * All Rights Reserved.
  52.  *}
  53.  
  54. USES MemTypes, Quickdraw, OSIntf, ToolIntf, PackIntf;
  55.  
  56. {$R-} {no range checking}
  57. {$D+} {Generate debug symbols}
  58.  
  59. CONST
  60.     menuBase = 128;            {we number our menus starting at 128}
  61.     
  62.     mainMenu = menuBase;    {main menu ID}
  63.         {we don't care about the Font/Style/Size item numbers; we}
  64.         {deal with choices in the submenus directly}
  65.         quitItem = 5;            {Quit item number}
  66.     
  67.     {the Font submenu ID}
  68.     fontMenu = mainMenu+1;        
  69.         {we retrieve the font name from the menu by item number,}
  70.         {then use GetFNum(the font name) to convert to family}
  71.         {number. The font Name should be stored with the document,}
  72.         {not the family number (because Font/DA Mover renumbers)}
  73.         
  74.     {the Style submenu ID}
  75.     styleMenu = fontMenu+1;        
  76.         {Styles are dealt with in the same order they appear in}
  77.         {the Quickdraw "Style" type. Sneaky, but consistent with}
  78.         {other Macintosh applications}
  79.         
  80.     {the Size submenu ID}
  81.     sizeMenu = styleMenu+1;         {the Size submenu ID}
  82.         {Sizes are retrieved as text from the menu item; this would}
  83.         {allows advanced users to edit the menu to add custom sizes}
  84.         
  85.     lastMenu = sizeMenu;        {the last menu resource}
  86.     firstSubMenu = fontMenu;     {the first submenu - see SetUpMenus}
  87.     
  88.     myWINDid = 128;            {our window template}
  89.     myALRTid = 128;         {our “need right machine & sys software” alert}
  90.     mySTRListID = 128;        {our string list}
  91.     
  92. VAR    myWindow: WindowPtr;        {our window pointer}
  93.     myEvent: EventRecord;        {Event record for GetNextEvent}    
  94.     myMenus: ARRAY [menuBase..lastMenu] OF MenuHandle; {our menus}
  95.     done: BOOLEAN;                {a flag that the user chose Quit}
  96.     dragRect: Rect;                {drag limit rectangle, for DragWindow}
  97.     
  98.     demoStr1, demoStr2,            {Strings to draw in our demo window}
  99.     demoStr3, demoStr4: Str255;    
  100.     
  101.     curFont: INTEGER;            {the current font number}
  102.     curSize: INTEGER;
  103.     curStyle: Style;
  104.     
  105.     i: INTEGER;                    {a temp}
  106.     mResult: LongInt;            {temp, result from MenuSelect}
  107.     
  108.     
  109. {*
  110.  * Once-only initialization for my menus
  111.  * (Thanks, Andy!)
  112.  *}
  113. PROCEDURE SetUpMenus;
  114. VAR i: INTEGER;
  115. BEGIN
  116.     {Get and insert each menu. The normal menus are inserted using zero}
  117.     {as the second parameter to InsertMenu; the submenus get inserted using}
  118.     {-1, to tell the Menu Manager to insert in the hierarchical menu list}
  119.     FOR i := menuBase TO lastMenu DO BEGIN
  120.         myMenus[i] := GetMenu(i); {get the menu from the resource}
  121.         
  122.         {insert it into a menu bar, one way or another}
  123.         IF i < firstSubMenu THEN
  124.             {it's a regular menu-bar menu}
  125.             InsertMenu(myMenus[i],0) {insert at end of normal menu list}
  126.         ELSE 
  127.             {it's a submenu}
  128.             InsertMenu(myMenus[i],-1); {insert hierarchical submenu}
  129.     END; {for each menu}
  130.     
  131.     {Add the fonts to the Font submenu}
  132.     AddResMenu(myMenus[fontMenu],'FONT'); {add fonts to the font menu}
  133.     
  134.     DrawMenuBar; {draw our menus' titles}
  135. END; {setupmenus}
  136.  
  137.  
  138. {*
  139.  * Handle a command, either from a command key or from
  140.  * a menu choice. MResult is the value returned by 
  141.  * MenuSelect or MenuKey.
  142.  *}
  143. PROCEDURE DoCommand (mResult: LONGINT);
  144. VAR theItem,theMenu: INTEGER;
  145.     tmpStr: Str255;
  146.     tmpLong: LongInt;
  147. BEGIN
  148.     theItem := LoWord(mResult); {extract the menu and item numbers}
  149.     theMenu := HiWord(mResult);    
  150.     CASE theMenu OF {which menu?}
  151.         mainMenu:  CASE theItem OF
  152.             {Note that we don't have any cases for the font, style, or}
  153.             {size menus here: those are handled independently, below. Only}
  154.             {normal items that appear in this menu are taken care of here.}
  155.             quitItem: done := TRUE; 
  156.         END; {mainMenu case}
  157.  
  158.         fontMenu: BEGIN
  159.             {Here, we just change a global font number. However, if your}
  160.             {application saves font information in its documents, you should}
  161.             {save the font name (rather than the number), to avoid remapping}
  162.             {because of Font/DA Mover number changes. Each time you open}
  163.             {such a document, use GetFNum to find the right "current number".}
  164.             GetItem(myMenus[fontMenu],theItem,tmpStr); {get the chosen font’s name}
  165.             GetFNum(tmpStr,curFont); {get the new font’s number, to our global}
  166.             InvalRect(thePort^.portRect); {remember that we need to redraw}
  167.         END; {fontMenu case}
  168.  
  169.         styleMenu: BEGIN
  170.             {The styles (other than Plain) are arranged in the}
  171.             {menu in the same order that they appear in the}
  172.             {Style type. If it's not plain, we can convert the}
  173.             {item number to the proper enumerated value for the style}
  174.             {and use set math to toggle the particular style}
  175.             {characteristic.}
  176.             IF theItem = 1 THEN curStyle := [] ELSE BEGIN {not 'Plain'}
  177.                 {it's not plain, so toggle this style characteristic}
  178.                 IF StyleItem(theItem-2) IN curStyle THEN
  179.                     {this style characteristic is currently on, so turn it off}
  180.                     curStyle := curStyle - [StyleItem(theItem-2)]
  181.                 ELSE
  182.                     {this style characteristic is currently on, so turn it off}
  183.                     curStyle := curStyle + [StyleItem(theItem-2)];
  184.             END;
  185.             InvalRect(thePort^.portRect); {remember that we need to redraw}
  186.         END; {styleMenu case}
  187.  
  188.         sizeMenu: BEGIN
  189.             {Read the size out of the menu item}
  190.             GetItem(myMenus[sizeMenu],theItem,tmpStr); {get the chosen string}
  191.             StringToNum(tmpStr,tmpLong); {convert to longint}
  192.             curSize := tmpLong; {convert it to integer}
  193.             InvalRect(thePort^.portRect); {remember that we need to redraw}
  194.         END; {sizeMenu case}
  195.         
  196.       END; {menu case}          
  197.     IF NOT done THEN HiliteMenu(0); {un-hilight the chosen menu item}
  198. END; {DoCommand}
  199.  
  200.  
  201. {*
  202.  * Main event loop
  203.  *}
  204. PROCEDURE MainEventLoop;
  205. VAR thePart, theHeight: INTEGER;
  206.     whichWindow: WindowPtr;
  207.     theChar: CHAR;
  208.     dragRect: Rect;
  209.     fInfo: FontInfo;
  210. BEGIN
  211.     done := FALSE; {we're not through yet!}
  212.     dragRect := screenBits.bounds;
  213.     
  214.     {Main event loop}
  215.     REPEAT  
  216.         SystemTask; {let the desk accs run}
  217.         IF GetNextEvent(everyEvent,myEvent) THEN WITH myEvent DO BEGIN
  218.             CASE what OF 
  219.                 mouseDown: BEGIN {"Click"}
  220.                     thePart := FindWindow(where,whichWindow);
  221.                     CASE thePart OF
  222.                         inSysWindow: SystemClick(myEvent,whichWindow);
  223.                         inMenuBar: DoCommand(MenuSelect(myEvent.where)); {nothing special here!}
  224.                         inDrag:    DragWindow(whichWindow,myEvent.where,dragRect);
  225.                     END; {case}
  226.                 END; {mouseDown}
  227.  
  228.                 keyDown, autoKey: BEGIN
  229.                     theChar := CHR(BitAnd(myEvent.message,charCodeMask));  {get the char}
  230.                     IF BitAnd(myEvent.modifiers,cmdKey) <> 0 THEN BEGIN
  231.                         DoCommand(MenuKey(theChar)); {pass it to the command handler}
  232.                     END ELSE BEGIN {not a command key}
  233.                         {ignore it}
  234.                     END; {if not command key}
  235.                 END; {keydown}
  236.  
  237.                 updateEvt: BEGIN {our window needs drawing}
  238.                     BeginUpdate(Windowptr(message));
  239.                     
  240.                     SetPort(myWindow); {make sure we're in our port}
  241.                     EraseRect(thePort^.portRect); {erase the old stuff}
  242.                     
  243.                     TextFont(curFont); {Set the right font/size/style}
  244.                     TextSize(curSize);
  245.                     TextFace(curStyle);
  246.                     
  247.                     GetFontInfo(fInfo);
  248.                     WITH fInfo DO theHeight := ascent+descent+leading;
  249.                     MoveTo(20,20+theHeight); DrawString(demoStr1);
  250.                     MoveTo(20,20+(theHeight*2)); DrawString(demoStr2);
  251.                     MoveTo(20,20+(theHeight*3)); DrawString(demoStr3);
  252.                     MoveTo(20,20+(theHeight*5)); DrawString(demoStr4);
  253.                                         
  254.                     EndUpdate(WindowPtr(message));
  255.                 END;
  256.             END; {case}
  257.         END; {if we got an event}
  258.         
  259.     UNTIL done; {keep it up until the user quits}
  260.     
  261. END; {maineventloop}
  262.  
  263.  
  264. {*
  265.  * Main
  266.  *}
  267. BEGIN
  268.     {Initialize all the usual managers}
  269.     InitGraf(@thePort);    
  270.     InitFonts;            
  271.     FlushEvents(everyEvent,0);
  272.     InitWindows;                
  273.     InitMenus;            
  274.     TEInit;
  275.     InitDialogs(NIL);            
  276.     InitCursor;            
  277.     
  278.     {Check to make sure we’re running on a machine}
  279.     {capable of supporting hierarchical menus; clearly,}
  280.     {we must do this before calling SetUpMenus!}
  281.     (** SysEnvirons(xxx); **)
  282.     IF FALSE THEN BEGIN {Sorry, see your dealer}
  283.         i := StopAlert(myALRTid,NIL); {put up the alert}
  284.         ExitToShell; {back to the finder}
  285.     END;
  286.     
  287.     curFont := 1; {Application font}
  288.     curSize := 0; {default size}
  289.     curStyle := []; {plain}
  290.     
  291.     SetUpMenus; {the oldest Macintosh subroutine!}
  292.     
  293.     {read the strings that we draw in our window}
  294.     {from a STR# resource}
  295.     GetIndString(demoStr1,mySTRListID,1);
  296.     GetIndString(demoStr2,mySTRListID,2);
  297.     GetIndString(demoStr3,mySTRListID,3);
  298.     GetIndString(demoStr4,mySTRListID,4);
  299.     
  300.     {Get our window}
  301.     myWindow := GetNewWindow(myWINDid,NIL,POINTER(-1));
  302.  
  303.     MainEventLoop; {handle events until the user quits}
  304. END.
  305.  
  306.  
  307.